-
Notifications
You must be signed in to change notification settings - Fork 12.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add the 'awaited' type operator #35998
Conversation
cc: @weswigham, @DanielRosenwasser, @ahejlsberg, @RyanCavanaugh (since suggested reviewers weren't working) |
Wouldn't a more general operator, something like |
Looks like the position of type argument declarations is not the best for inserting modifiers. So if we use function f<T extends number>(x: unwrapped PrimiseLike<T>) {
} |
@falsandtru I think there are complications in the syntax. For example what if you need to unwrap a type with more than one type parameter, you need to be able to specify the position of the argument that you are unwrapping by. |
If nested promises are allowed, then shouldn't the following be allowed? const expected: Promise<string> = undefined as Promise<Promise<string>>;
~~~~~~~~
!!! error TS2322: Type 'Promise<Promise<string>>' is not assignable to type 'Promise<string>'. What do you think of #35284 instead? |
Its not that simple, for several reasons:
All of these behaviors are already handled by |
We already investigated adding an |
Is it necessary for Why make it recursive if |
I can't help but feel as though we're jumping too quickly to a syntactic solution here... From what I understand of this issue, we could hypothetically solve this problem without new syntax if we had a "canonized" story around recursive conditional types. Is that a reasonable assessment? Anecdotally, I've seen the topic of recursive types come up a lot, and I know there are a good few libraries available that implement various forms of the hack @rbuckton mentioned. It definitely feels to me as though there's need for a more general feature (or set of features) in the recursive type space. Has there been any exploration/ideation into that space that would address this specific issue as part of a more general solution? :) |
I would argue that two years isn't "jumping too quickly". Adding |
That wasn't what was out of scope. What I was calling out was that writing |
I agree, |
This is true. 🙂
Oh 100%. I don't mean to trivialise recursive types, as I'm very aware of the headaches they bring. 🙂 But people are writing them already (myself included), and there are libraries full of them. If we could build out that feature space a bit, then it's possible we wouldn't need special |
What are the cases where a non-recursive solution isn't equivalent? Why are those cases important? Do they justify adding a new kind of type? |
@rbuckton Can’t it just be |
My argument is that
It's clear why #21613 was investigated and rejected, if a recursive solution was assumed. What I'm searching for and haven't found yet, is why a non-recursive solution was rejected? |
I’d prefer if the type operator was just |
@jablko: The problem of |
@rbuckton Here they are:Comparison Report - master..35998
System
Hosts
Scenarios
|
...
This will result in an error as we haven't changed inference here. However, if you change your example in this way: declare function f<T>(x: Promise<T>): Promise<awaited T extends number ? true : false>;
const expected: Promise<true> = f(undefined as any as Promise<Promise<number>>); Then you get the correct result. |
@DanielRosenwasser in the DT test I'm seeing a lot of this from JQuery: ../jquery/misc.d.ts(1205,15): error TS2430: Interface 'PromiseBase<TR, TJ, TN, UR, UJ, UN, VR, VJ, VN, SR, SJ, SN>' incorrectly extends interface '_Promise<TR>'.
Types of property 'then' are incompatible.
Type '{ <ARD = never, AJD = never, AND = never, BRD = never, BJD = never, BND = never, CRD = never, CJD = never, CND = never, RRD = never, RJD = never, RND = never, ARF = never, AJF = never, ANF = never, BRF = never, BJF = never, BNF = never, CRF = never, CJF = never, CNF = never, RRF = never, RJF = never, RNF = never, AR...' is not assignable to type '<TResult1 = TR, TResult2 = never>(onfulfilled?: ((value: TR) => TResult1 | PromiseLike<TResult1>) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike<...>) | null | undefined) => _Promise<...>'. I'm investigating whether this is something I can fix in this PR or if I will just need to make a change to JQuery. |
Upon investigation, I think the fix will need to be in the JQuery types, due to the fact they are extending from both |
Would it make sense to accommodate type Awaited<T> = Promise<T> extends Promise<infer U> ? U : never; Assuming |
Currently, no. We're not doing anything special for conditionals here, and as I mentioned the problem would be easily addressable by using
I'm not sure what the difference would be, and I don't see how we could choose to assume the |
There are some known breaks on DT that I am planning to address in parallel. |
To close multiple issues or PRs, you need to write the keyword before each of them: https://help.github.com/en/github/managing-your-work-on-github/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword |
@ExE-Boss thanks, I went ahead and closed them manually. |
Summary: Add the new keyword "awaited": microsoft/TypeScript#35998 Reviewers: #framework_syntax_highlighting, dhaumann, cullmann Reviewed By: #framework_syntax_highlighting, cullmann Subscribers: kwrite-devel, kde-frameworks-devel Tags: #kate, #frameworks Differential Revision: https://phabricator.kde.org/D28814
It has been over two years since we decided not to move forward with #17077 due to the advent of conditional types, however in that time we have still been unable to devise a reliable mechanism for defining a recursive conditional type that can properly handle the type-side of ECMAScript's
await
and nativePromise
unwrapping behavior.Solutions like
Awaited<T>
were untenable given that the only way to make the conditional type "recursive" was to introduce complex type like the following:However, there are a number of problems with this approach:
{ 0: ... }[U extends any ? 0 : never]
is doing in this alias.As a result, I would like to reintroduce the
awaited
type operator from #17077 to meet our needs for a mechanism to recursively unwrap a Promise-like type for methods likePromise.all
,Promise.race
,Promise.allSettled
,Promise.prototype.then
, andPromise.prototype.catch
. Theawaited
type operator has the following semantics:awaited
operator is resolved immediately:awaited number
isnumber
awaited Promise<number>
isnumber
awaited
operator is deferred until instantiated with a non-generic:awaited Promise<T>
isawaited T
awaited Promise<Obj[K]>
isawaited Obj[K]
awaited S
is assignable to anawaited T
ifS
is assignable toT
:T
could be subtyped with anS
that has athen()
. However, this is not likely to be a common practice and is no less permissive than the pseudo-recursive conditional type approach.awaited S
is related toT
ifawaited C
is related toT
, whereC
is the constraint ofS
:This will help us to merge PRs such as #33707 without needing to introduce a non-recursive or pseudo-recursive
Awaited<T>
type.Fixes: #27711, #31722
Closes: #36435, #36368.